package com.atues.thread.lock;

public class ProComLock {
    public static void main(String[] args) {
        Data data = new Data();
        new Thread(() -> {
            for(int i = 0 ; i < 10 ; i++){
                try {
                    data.produce();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "生产者1").start();
        new Thread(()->{
            for(int i = 0 ; i < 10 ; i++){
                try {
                    data.consume();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "消费者1").start();
        new Thread(() -> {
            for(int i = 0 ; i < 10 ; i++){
                try {
                    data.produce();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "生产者2").start();
        new Thread(()->{
            for(int i = 0 ; i < 10 ; i++){
                try {
                    data.consume();
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }
            }
        }, "消费者2").start();
    }
}

class Data{
    private int num;

    public synchronized void produce() throws InterruptedException {
        while (num != 0) {
            wait();
        }
        num++;
        System.out.println(Thread.currentThread().getName() + "生产了一个数据：" + num);

        notifyAll();
    }

    public synchronized void consume() throws InterruptedException {
        //if 判断条件可能因多线程并发导致虚假唤醒（Spurious Wakeup），应改用 while 循环检查条件
        //而且其实发现多余一个生产者消费者就会出现问题，这里两个消费者假如都进入了if(num == 0)的判断中,
        // 此时生产者生产了一个数据，两个消费者都被唤醒了，都会执行消费操作，导致num--两次
        //官方建议使用 while 循环来避免虚假唤醒
        while (num == 0) {
            wait();//进入等待状态 WAITING
        }
        num--;
        System.out.println(Thread.currentThread().getName() + "消费了一个数据：" + num);

        notifyAll();
    }
}
